// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_BROWSER_PPAPI_PLUGIN_PROCESS_HOST_H_
#define CONTENT_BROWSER_PPAPI_PLUGIN_PROCESS_HOST_H_

#include <stdint.h>

#include <memory>
#include <queue>
#include <string>
#include <vector>

#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/process/process.h"
#include "base/strings/string16.h"
#include "content/browser/renderer_host/pepper/browser_ppapi_host_impl.h"
#include "content/browser/renderer_host/pepper/pepper_message_filter.h"
#include "content/public/browser/browser_child_process_host_delegate.h"
#include "content/public/browser/browser_child_process_host_iterator.h"
#include "ipc/ipc_sender.h"
#include "ppapi/shared_impl/ppapi_permissions.h"

namespace content {
class BrowserChildProcessHostImpl;
class ResourceContext;
struct PepperPluginInfo;

// Process host for PPAPI plugin and broker processes.
// When used for the broker, interpret all references to "plugin" with "broker".
class PpapiPluginProcessHost : public BrowserChildProcessHostDelegate,
                               public IPC::Sender {
public:
    class Client {
    public:
        // Gets the information about the renderer that's requesting the channel.
        // If |renderer_handle| is base::kNullProcessHandle, this channel is used by
        // the browser itself.
        virtual void GetPpapiChannelInfo(base::ProcessHandle* renderer_handle,
            int* renderer_id)
            = 0;

        // Called when the channel is asynchronously opened to the plugin or on
        // error. On error, the parameters should be:
        //   base::kNullProcessHandle
        //   IPC::ChannelHandle(),
        //   0
        virtual void OnPpapiChannelOpened(
            const IPC::ChannelHandle& channel_handle,
            base::ProcessId plugin_pid,
            int plugin_child_id)
            = 0;

        // Returns true if the current connection is incognito.
        virtual bool Incognito() = 0;

    protected:
        virtual ~Client() { }
    };

    class PluginClient : public Client {
    public:
        // Returns the resource context for the renderer requesting the channel.
        virtual ResourceContext* GetResourceContext() = 0;

    protected:
        ~PluginClient() override { }
    };

    class BrokerClient : public Client {
    protected:
        ~BrokerClient() override { }
    };

    ~PpapiPluginProcessHost() override;

    static PpapiPluginProcessHost* CreatePluginHost(
        const PepperPluginInfo& info,
        const base::FilePath& profile_data_directory);
    static PpapiPluginProcessHost* CreateBrokerHost(
        const PepperPluginInfo& info);

    // Notification that a PP_Instance has been created and the associated
    // renderer related data including the RenderView/Process pair for the given
    // plugin. This is necessary so that when the plugin calls us with a
    // PP_Instance we can find the RenderView associated with it without trusting
    // the plugin.
    static void DidCreateOutOfProcessInstance(
        int plugin_process_id,
        int32_t pp_instance,
        const PepperRendererInstanceData& instance_data);

    // The opposite of DIdCreate... above.
    static void DidDeleteOutOfProcessInstance(int plugin_process_id,
        int32_t pp_instance);

    // Notification that a Plugin instance has been throttled or unthrottled.
    static void OnPluginInstanceThrottleStateChange(int plugin_process_id,
        int32_t pp_instance,
        bool is_throttled);

    // Returns the instances that match the specified process name.
    // It can only be called on the IO thread.
    static void FindByName(const base::string16& name,
        std::vector<PpapiPluginProcessHost*>* hosts);

    // IPC::Sender implementation:
    bool Send(IPC::Message* message) override;

    // Opens a new channel to the plugin. The client will be notified when the
    // channel is ready or if there's an error.
    void OpenChannelToPlugin(Client* client);

    BrowserPpapiHostImpl* host_impl() { return host_impl_.get(); }
    const BrowserChildProcessHostImpl* process() { return process_.get(); }
    const base::FilePath& plugin_path() const { return plugin_path_; }
    const base::FilePath& profile_data_directory() const
    {
        return profile_data_directory_;
    }

#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX)
    // Launch the zygote early in the browser startup.
    static void EarlyZygoteLaunch();
#endif // defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX)

    // The client pointer must remain valid until its callback is issued.

private:
    class PluginNetworkObserver;

    // Constructors for plugin and broker process hosts, respectively.
    // You must call Init before doing anything else.
    PpapiPluginProcessHost(const PepperPluginInfo& info,
        const base::FilePath& profile_data_directory);
    PpapiPluginProcessHost();

    // Actually launches the process with the given plugin info. Returns true
    // on success (the process was spawned).
    bool Init(const PepperPluginInfo& info);

    void RequestPluginChannel(Client* client);

    void OnProcessLaunched() override;

    void OnProcessCrashed(int exit_code) override;
    bool OnMessageReceived(const IPC::Message& msg) override;
    void OnChannelConnected(int32_t peer_pid) override;
    void OnChannelError() override;

    void CancelRequests();

    // IPC message handlers.
    void OnRendererPluginChannelCreated(const IPC::ChannelHandle& handle);
    void OnFieldTrialActivated(const std::string& trial_name);

    // Handles most requests from the plugin. May be NULL.
    scoped_refptr<PepperMessageFilter> filter_;

    ppapi::PpapiPermissions permissions_;
    std::unique_ptr<BrowserPpapiHostImpl> host_impl_;

    // Observes network changes. May be NULL.
    std::unique_ptr<PluginNetworkObserver> network_observer_;

    // Channel requests that we are waiting to send to the plugin process once
    // the channel is opened.
    std::vector<Client*> pending_requests_;

    // Channel requests that we have already sent to the plugin process, but
    // haven't heard back about yet.
    std::queue<Client*> sent_requests_;

    // Path to the plugin library.
    base::FilePath plugin_path_;

    // Path to the top-level plugin data directory (differs based upon profile).
    base::FilePath profile_data_directory_;

    const bool is_broker_;

    std::unique_ptr<BrowserChildProcessHostImpl> process_;

    DISALLOW_COPY_AND_ASSIGN(PpapiPluginProcessHost);
};

class PpapiPluginProcessHostIterator
    : public BrowserChildProcessHostTypeIterator<
          PpapiPluginProcessHost> {
public:
    PpapiPluginProcessHostIterator()
        : BrowserChildProcessHostTypeIterator<
            PpapiPluginProcessHost>(PROCESS_TYPE_PPAPI_PLUGIN)
    {
    }
};

class PpapiBrokerProcessHostIterator
    : public BrowserChildProcessHostTypeIterator<
          PpapiPluginProcessHost> {
public:
    PpapiBrokerProcessHostIterator()
        : BrowserChildProcessHostTypeIterator<
            PpapiPluginProcessHost>(PROCESS_TYPE_PPAPI_BROKER)
    {
    }
};

} // namespace content

#endif // CONTENT_BROWSER_PPAPI_PLUGIN_PROCESS_HOST_H_
